package com.linkdood.app.component;

import com.linkdood.app.config.WebSocketConfig;
import com.linkdood.app.service.SystemNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * websocket 配置文件
 * 在 @ServerEndpoint 下， 使用 @AutoWired 是无法注入成功的，具体原因如下：
 * https://my.oschina.net/u/4321566/blog/4302516
 * 解决办法见：
 * https://blog.csdn.net/qq_28289867/article/details/80423042
 */
@Slf4j
@ServerEndpoint(value = "/websocket/{id}", configurator = WebSocketConfig.class)
@Component
public class WebSocketServer {

    /**
     * 静态变量，用来记录当前在线连接数
     */
    private static int onlineCount = 0;

    private static final ConcurrentHashMap<String, WebSocketServer> webSocketSet = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 用戶id, 具有唯一性
     */
    private String sid = "";

    private static ApplicationContext applicationContext;

    private SystemNoticeService systemNoticeService;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServer.applicationContext = applicationContext;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("id") String id, Session session) {
        this.session = session;
        this.sid = id;

        webSocketSet.put(id, this);
        addOnlineCount();
        log.info("user {} join！online number is {}", id, getOnlineCount());
        try {
            //此处是解决无法注入的关键
            systemNoticeService = applicationContext.getBean(SystemNoticeService.class);
            int messageCounts = systemNoticeService.countUnReadNotices();
            sendMessage(Integer.toString(messageCounts));
        } catch (IOException e) {
            log.error("{}", e);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
        log.info("a user close！online number is {}", getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("receive sid = {} message : {}", sid, message);
        try {
            sendMessage("ok");
        } catch (IOException e) {
            log.error("{}", e);
        }
    }

    /**
     * 异常处理
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("error");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 发送给指定客户端
     */
    public boolean sendToUser(String message, String sendUserId) throws IOException {
        if (StringUtils.isEmpty(sendUserId)) {
            for (String key : webSocketSet.keySet()) {
                try {
                    webSocketSet.get(key).sendMessage(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            if (webSocketSet.get(sendUserId) == null) {
                //如果用户不在线则返回不在线信息给自己
                log.error("not found user");
                return false;
            }
            if (!sid.equals(sendUserId)) {
                log.error("not found user");
                return false;
            }
            webSocketSet.get(sendUserId).sendMessage(message);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("send message success");
            return true;
        }
        return true;
    }


    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}
